#include "GIFDecoder.h"
#include "LZW.h"

/**
 * \fn GIFFile::GIFSignature::GIFSignature()
 *
 * \brief Default constructor. Setup default size of GIF Signature.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
**/
GIFFile::GIFSignature::GIFSignature(){
	signatureSize = 6;
};

/**
 * \fn bool GIFFile::GIFSignature::readGIFSignature(istream &in)
 *
 * \brief Reads a GIF signature.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \param in Input Stream
 *
 * \return true if it succeeds, false if it fails or GIF Signature != GIF87a.
**/
bool GIFFile::GIFSignature::readGIFSignature(istream &in){
	signature = "";
	BYTE temp;
	for (int i=0; i<signatureSize; i++){
		if (!get(in, temp))
			return false;
		signature = signature + char(temp);
	}			
	if (signature.compare(0,3,"GIF") != 0){
		cout << "Cannot recognize GIF file" << endl;
		return false;
	}
	if (signature != "GIF87a"){
		cout << "The GIF decoder doesn't support the file with signature" << signature << endl;
		return false;
	}

	return true;
};

/**
 * \fn void GIFFile::GIFSignature::printGIFSignature()
 *
 * \brief Print GIF signature.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
**/
void GIFFile::GIFSignature::printGIFSignature(){
	cout << "GIF SIGNATURE" << endl;
	cout << "Version: " << signature << endl;
	cout << endl;
};

/**
 * \fn bool GIFFile::ScreenDescriptor::readScreenDescriptor(istream &in)
 *
 * \brief Reads a Screen Descriptor.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \param in Input Stream
 *
 * \return true if it succeeds, false if it fails.
**/
bool GIFFile::ScreenDescriptor::readScreenDescriptor(istream &in){			
	if (!get(in, Width))		// Read Width
		return false;			
	if (!get(in, Height))		// Read Height
		return false;
	
	BYTE Packed;				// Read Screen and Color Map Information
	if (!get(in, Packed))
		return false;			
	M = Packed >> 7;			
	cr = Packed >> 4;			
	cr = cr & 0x7;
	pixel = Packed & 0x7;
	
	if (!get(in, background))	// Read Background
		return false;
	
	if (!get(in, AspectRatio))	// Read last byte (00)
		return false;

	return true;
};

/**
 * \fn void GIFFile::ScreenDescriptor::printScreenDescriptor()
 *
 * \brief Print Screen Descriptor.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
**/
void GIFFile::ScreenDescriptor::printScreenDescriptor(){
	cout << "SCREEN DESCRIPTOR" << endl;
	cout << "Width: " << Width << endl;
	cout << "Height: " << Height << endl;
	cout << "M: " << M << " (If M = 1 Global color map follows Descriptor)" << endl;
	cout << "cr: " << (cr+1) << " (# bits of color resolution)" << endl;
	cout << "pixel: " << (pixel+1) << " (# bits/pixel in image)" << endl;
	cout << "background: " << (int)background << " (Color index of screen background) " << endl;			
	cout << endl;
};

/**
 * \fn bool GIFFile::ColorMapEntry::getColorMapEntry(istream &in)
 *
 * \brief Gets a color map entry.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \param in Input Stream
 *
 * \return true if it succeeds, false if it fails.
**/
bool GIFFile::ColorMapEntry::getColorMapEntry(istream &in) {								
	if (!get(in, rgbRed)) return false;				
	if (!get(in, rgbGreen)) return false;
	if (!get(in, rgbBlue)) return false;
		
	return true;
};

/**
 * \fn GIFFile::GlobalColorMap::GlobalColorMap()
 *
 * \brief Default constructor.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
**/
GIFFile::GlobalColorMap::GlobalColorMap(){
	bitPerPixel = 8;									// By default we have 8 bits for pixel index
	_ColorMap = 0;						
};

/**
 * \fn GIFFile::GlobalColorMap::~GlobalColorMap()
 *
 * \brief Destructor.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
**/
GIFFile::GlobalColorMap::~GlobalColorMap(){
	delete _ColorMap;
};

/**
 * \fn bool GIFFile::GlobalColorMap::readGlobalColorMap(istream &in)
 *
 * \brief Reads a Global Color Map.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \param in Input Stream
 *
 * \return true if it succeeds, false if it fails.
**/
bool GIFFile::GlobalColorMap::readGlobalColorMap(istream &in){					
	int mapSize = 1 << bitPerPixel;						// mapSize = 2^bitPerPixel
	_ColorMap = new ColorMapEntry[mapSize];

	for (int i=0; i<mapSize; i++){
		if (!_ColorMap[i].getColorMapEntry(in))
			return false;
	}

	return true;
};

/**
 * \fn bool GIFFile::GlobalColorMap::buildDefaultGlobalColorMap()
 *
 * \brief Builds the default Global Color Map.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \return true if it succeeds, false if it fails.
**/
bool GIFFile::GlobalColorMap::buildDefaultGlobalColorMap(){
	int mapSize = 1 << bitPerPixel;						// mapSize = 2^bitPerPixel
	_ColorMap = new ColorMapEntry[mapSize];			
	for (int i=0; i<mapSize; i++){
		_ColorMap[i].rgbRed = _ColorMap[i].rgbGreen = _ColorMap[i].rgbBlue = (BYTE)i;				
	}

	return true;
};

/**
 * \fn void GIFFile::GlobalColorMap::printGlobalColorMap()
 *
 * \brief Print Global Color Map.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
**/
void GIFFile::GlobalColorMap::printGlobalColorMap(){
	cout << "GLOBAL COLOR MAP" << endl;

	int mapSize = 1 << bitPerPixel;
	int i;
	for (i=0; i<mapSize; i++){
		cout << dec << "Color Map Entry[" << i << "]: " << "#" << setw(2) << setfill('0') << hex << uppercase << int(_ColorMap[i].rgbRed) << setw(2) << int(_ColorMap[i].rgbGreen) << setw(2) << int(_ColorMap[i].rgbBlue) << endl;
		if (i > 0 && i%10 == 0)
			break;
	}

	cout << "..." << endl;			
	cout << dec << "Color Map Entry[" << (mapSize-1) << "]: " << "#" << setw(2) << setfill('0') << hex << uppercase << int(_ColorMap[mapSize-1].rgbRed) <<  setw(2) << int(_ColorMap[mapSize-1].rgbGreen) << setw(2) << int(_ColorMap[mapSize-1].rgbBlue) << endl;
	cout << dec << endl;
};

/**
 * \fn bool GIFFile::LocalImageDescriptor::readLocalImageDescriptor(istream &in)
 *
 * \brief Reads a Local Image Descriptor.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \param in Input Stream
 *
 * \return true if it succeeds, false if it fails.
**/
bool GIFFile::LocalImageDescriptor::readLocalImageDescriptor(istream &in){			
	if (!get(in, ImageLeft))					// Read X position of image on the display
		return false;			
	if (!get(in, ImageTop))						// Read Y position of image on the display
		return false;			
	if (!get(in, ImageWidth))					// Read Width of the image in pixels
		return false;			
	if (!get(in, ImageHeight))					// Read Height of the image in pixels
		return false;
	
	BYTE Packed;								// Read Image and Color Table Data Information
	if (!get(in, Packed))
		return false;			

	M = Packed >> 7;			
	I = Packed >> 6;			
	I = I & 0x1;
	pixel = Packed & 0x7;			

	return true;
};

/**
 * \fn void GIFFile::LocalImageDescriptor::printLocalImageDescriptor()
 *
 * \brief Print Local Image Descriptor.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
**/
void GIFFile::LocalImageDescriptor::printLocalImageDescriptor(){
	cout << "LOCAL IMAGE DESCRIPTOR" << endl;
	cout << "Left, X: " << ImageLeft << endl;
	cout << "Top, Y: " << ImageTop << endl;
	cout << "Width: " << ImageWidth << endl;
	cout << "Height: " << ImageHeight << endl;
	if (M == 0)
		cout << "M: " << M << " (Use global color map, ignore 'pixel')" << endl;
	else
		cout << "M: " << M << " (Local color map follows, use 'pixel')" << endl;
	if (I == 0)
		cout << "I: " << I << " (Image formatted in Sequential order)" << endl;
	else
		cout << "I: " << I << " (Image formatted in Interlaced order)" << endl;
	cout << "pixel: " << (pixel+1) << " (# bits/pixel for this image)" << endl;			
	cout << endl;
};

/**
 * \fn GIFFile::LocalColorMap::LocalColorMap()
 *
 * \brief Default constructor.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
**/
GIFFile::LocalColorMap::LocalColorMap(){
	bitPerPixel = 8;									// By default we have 8 bits for pixel index
	_ColorMap = 0;						
};

/**
 * \fn GIFFile::LocalColorMap::~LocalColorMap()
 *
 * \brief Destructor.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
**/
GIFFile::LocalColorMap::~LocalColorMap(){
	delete _ColorMap;
};

/**
 * \fn bool GIFFile::LocalColorMap::readLocalColorMap(istream &in)
 *
 * \brief Reads a Local Color Map.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \param in Input Stream
 *
 * \return true if it succeeds, false if it fails.
**/
bool GIFFile::LocalColorMap::readLocalColorMap(istream &in){					
	int mapSize = 1 << bitPerPixel;						// mapSize = 2^bitPerPixel
	_ColorMap = new ColorMapEntry[mapSize];

	for (int i=0; i<mapSize; i++){
		if (!_ColorMap[i].getColorMapEntry(in))
			return false;
	}

	return true;
};		

/**
 * \fn bool GIFFile::RasterData::readRasterData(istream &in, BYTE *ImageIndices)
 *
 * \brief Reads a Raster Data (LZW coded bytes).
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \param in Input Stream
 * \param ImageIndices BYTE buffer containing decoded Pixel Indices
 *
 * \return true if it succeeds, false if it fails.
**/
bool GIFFile::RasterData::readRasterData(istream &in, BYTE *ImageIndices){				

	if (!get(in,CodeSize))							// Read Code Size
		return false;						

	// Define the buffer containing encoded bytes (Data Bytes)
	BYTE *EncodedImageData;
	EncodedImageData = new BYTE[255];	

	// The LZW GIF Decoder
	LZW _LZW;
	_LZW.setInitialCodeSize(CodeSize);
	_LZW.initializeAllWorkVariables();
			
	BYTE BlockByteCount;							// How many bytes we have inside Data Block
	BYTE ch;										// Help variable, used to get one byte from file

	// Start reading every Data Block of current Image
	while (true){		
		if (!get(in,BlockByteCount))				// Read Block Byte Count
			return false;								
		if (BlockByteCount == 0)					// We have read all Data Blocks (Zero Byte Count)
			break;
		
		for (int i=0; i<(int)BlockByteCount; i++){	
			if (!get(in,ch))						// Read one encoded byte at time
				return false;
			EncodedImageData[i] = ch;				// Put read byte into the buffer EncodedImageData
		}	
		
		// Decode one Data Block at time end put decoded bytes (Pixel Indicies) into the buffer ImageIndices 
		if (!_LZW.readGIFDataBlock(EncodedImageData, BlockByteCount, ImageIndices))
			return false;				
	}			
	
	delete EncodedImageData;

	return true;
};

/**
 * \fn bool GIFFile::RasterData::Interlaced2Sequential(BYTE *ImageIndices, int ImageWidth,
 * int ImageHeight)
 *
 * \brief Convert rows from Interlaced order to Sequential order.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \param ImageIndices BYTE buffer containing decoded Pixel Indices
 * \param ImageWidth Width of the image.
 * \param ImageHeight Height of the image.
 *
 * \return true if it succeeds, false if it fails.
**/
bool GIFFile::RasterData::Interlaced2Sequential(BYTE *ImageIndices, int ImageWidth, int ImageHeight){
	int ImageSize = ImageWidth * ImageHeight;
	BYTE *SequentialImageIndices = new BYTE[ImageSize];			// Buffer where we put rows in Sequential order
	
	int currentIndex = 0;
	int currentRow = 0;
	for (int i=0; i<ImageHeight/8; i++){						/* Interlace Pass 1 */
		for (int j=0; j<ImageWidth; j++){
			SequentialImageIndices[currentIndex] = ImageIndices[currentRow*ImageWidth+j];
			currentIndex++;
		}
		currentRow++;
		currentIndex = currentIndex + (ImageWidth * 7);
	}

	currentIndex = ImageWidth * 4;
	for (int i=0; i<ImageHeight/8; i++){						/* Interlace Pass 2 */
		for (int j=0; j<ImageWidth; j++){
			SequentialImageIndices[currentIndex] = ImageIndices[currentRow*ImageWidth+j];
			currentIndex++;
		}
		currentRow++;
		currentIndex = currentIndex + (ImageWidth * 7);
	}
	
	currentIndex = ImageWidth * 2;
	for (int i=0; i<ImageHeight/4; i++){						/* Interlace Pass 3 */
		for (int j=0; j<ImageWidth; j++){
			SequentialImageIndices[currentIndex] = ImageIndices[currentRow*ImageWidth+j];
			currentIndex++;
		}
		currentRow++;
		currentIndex = currentIndex + (ImageWidth * 3);
	}

	currentIndex = ImageWidth;
	for (int i=0; i<ImageHeight/2; i++){						/* Interlace Pass 4 */
		for (int j=0; j<ImageWidth; j++){
			SequentialImageIndices[currentIndex] = ImageIndices[currentRow*ImageWidth+j];
			currentIndex++;
		}
		currentRow++;
		currentIndex = currentIndex + ImageWidth; 
	}

	for (int i=0; i<ImageSize; i++){							// Update ImageIndices to contain rows in Sequential order
		ImageIndices[i] = SequentialImageIndices[i];
	}
	delete SequentialImageIndices;

	return true;
}

/**
 * \fn GIFFile::GIFFile()
 *
 * \brief Default constructor.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
**/
GIFFile::GIFFile(){
	ImageIndices = 0;
	ColorPalette = 0;
};

/**
 * \fn GIFFile::~GIFFile()
 *
 * \brief Destructor.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
**/
GIFFile::~GIFFile(){
	delete ImageIndices;
	delete ColorPalette;
};

/**
 * \fn bool GIFFile::readGIFFile(istream &in)
 *
 * \brief Read and decode GIF file.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \param in Input Stream
 * 		  
 * \return true if it succeeds, false if it fails.
**/
bool GIFFile::readGIFFile(istream &in){
	// Read GIF SIGNATURE
	if (!_GIFSignature.readGIFSignature(in)){
		cout << "GIF SIGNATURE error" << endl;
		return false;				
	}

	// Read SCREEN DESCRIPTOR
	if (!_ScreenDescriptor.readScreenDescriptor(in)){
		cout << "SCREEN DESCRIPTOR error" << endl;
		return false;		
	}

	// Read GLOBAL COLOR MAP only if the field M of ScreenDescriptor = 1
	if (_ScreenDescriptor.M == 1){
		_GlobalColorMap.bitPerPixel = _ScreenDescriptor.pixel + 1;
		if (!_GlobalColorMap.readGlobalColorMap(in)){
			cout << "GLOBAL COLOR MAP error" << endl;
			return false;			
		}
	}else{
		// GIF standard says to provide an internal default Palette. This can cause inaccurate color rendition!
		// If the field M of ScreenDescriptor = 0, create Color Map internally			
		_GlobalColorMap.bitPerPixel = _ScreenDescriptor.pixel + 1;
		if (!_GlobalColorMap.buildDefaultGlobalColorMap())
			return false;									
	}					

	// Define current Color Map
	ColorMapEntry *_ColorMap = 0;		

	/* Each Image Descriptor is introduced by an image separator character. The role of the Image
	Separator is simply to provide a synchronization character to introduce an Image Descriptor */
	BYTE ImageSeparator = ',';						// Image Separator must be 0x2C (',')

	/* GIF decoder will process the end of GIF mode when the character 0x3B hex or ';' is found */
	BYTE GIFTerminator = ';';						// GIF Terminator must be 0x3B (';')

	BYTE ch;
	while (true){												
		while (true){								// Read one byte at time until we find ImageSeparator or EOF
			if (!get(in, ch))
				break;
			if (ch == ImageSeparator)				
				break;
		}
		
		if (ch == ImageSeparator){					// Continue if we have found ImageSeparator						
			// Read LOCAL IMAGE DESCRIPTOR
			_LocalImageDescriptor.readLocalImageDescriptor(in);				

			// Read LOCAL COLOR MAP only if the field M of Local Image Descriptor = 1
			if (_LocalImageDescriptor.M == 1){
				_LocalColorMap.bitPerPixel = _LocalImageDescriptor.pixel + 1;
				if (!_LocalColorMap.readLocalColorMap(in))
					return false;	
				// Setup for current Image the Local Color Map
				_ColorMap = _LocalColorMap._ColorMap;
			}else
				// Setup for current Image the Global Color Map if the Image doesn't have its own
				_ColorMap = _GlobalColorMap._ColorMap;							

			// Read RASTER DATA				
			int imagePixels = (int)_LocalImageDescriptor.ImageWidth * (int)_LocalImageDescriptor.ImageHeight;	
			delete ImageIndices;
			ImageIndices = new BYTE[imagePixels];						// Initialize the buffer
			if (!_RasterData.readRasterData(in, ImageIndices)){
				cout << "RASTER DATA error" << endl;
				return false;														
			}
			
			if (_LocalImageDescriptor.I == 1){						// Image formatted in Interlaced order
				if (!_RasterData.Interlaced2Sequential(ImageIndices, (int)_LocalImageDescriptor.ImageWidth, (int)_LocalImageDescriptor.ImageHeight))
					return false;	
			}								

			if (!get(in, ch))
				return false;
			if (ch == GIFTerminator){		// We have reached the end of GIF file				
				return true;
			}else
				return true;				// If we have another Image - we don't process it (Option not supported)
							
		}

	}
	delete _ColorMap;

	return true;
};

/**
 * \fn void GIFFile::printGIFFile()
 *
 * \brief Print GIF file (Signature, Screen Descriptor, Global Color Map, Local Image Descriptor).
 *
 * \author Maksym Rybak
 * \date 17.03.2011
**/
void GIFFile::printGIFFile(){
	_GIFSignature.printGIFSignature();
	_ScreenDescriptor.printScreenDescriptor();
	_GlobalColorMap.printGlobalColorMap();
	_LocalImageDescriptor.printLocalImageDescriptor();
};

/**
 * \fn WORD GIFFile::getImageWidth()
 *
 * \brief Gets the image width.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \return The image width.
**/
WORD GIFFile::getImageWidth(){											
	return _LocalImageDescriptor.ImageWidth;
}

/**
 * \fn WORD GIFFile::getImageHeight()
 *
 * \brief Gets the image height.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \return The image height.
**/
WORD GIFFile::getImageHeight(){											
	return _LocalImageDescriptor.ImageHeight;
}

/**
 * \fn BYTE *GIFFile::getImageIndices()
 *
 * \brief Gets the image indices.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \return Pointer to the buffer containing image indices.
**/
BYTE *GIFFile::getImageIndices(){										
	return ImageIndices;
}

/**
 * \fn ColorPaletteEntry *GIFFile::getColorMap()
 *
 * \brief Gets the color map.
 *
 * \author Maksym Rybak
 * \date 17.03.2011
 *
 * \return Pointer to the buffer containing color map entries.
**/
ColorPaletteEntry *GIFFile::getColorMap(){								
	int paletteSize;		

	if (_LocalImageDescriptor.M == 0){							// If Image doesn't have its own Color Map
		paletteSize = 1 << _GlobalColorMap.bitPerPixel;
		delete ColorPalette;
		ColorPalette = new ColorPaletteEntry[paletteSize];
		for (int i=0; i<paletteSize; i++){
			ColorPalette[i].rgbRed = _GlobalColorMap._ColorMap[i].rgbRed;
			ColorPalette[i].rgbGreen = _GlobalColorMap._ColorMap[i].rgbGreen;
			ColorPalette[i].rgbBlue = _GlobalColorMap._ColorMap[i].rgbBlue;
		}			
	}else{														// Image has Local Color Map
		paletteSize = 1 << _LocalColorMap.bitPerPixel;
		delete ColorPalette;
		ColorPalette = new ColorPaletteEntry[paletteSize];
		for (int i=0; i<paletteSize; i++){
			ColorPalette[i].rgbRed = _LocalColorMap._ColorMap[i].rgbRed;
			ColorPalette[i].rgbGreen = _LocalColorMap._ColorMap[i].rgbGreen;
			ColorPalette[i].rgbBlue = _LocalColorMap._ColorMap[i].rgbBlue;
		}
	}		

	return ColorPalette;	
};

